#ifdef __GNUG__
#pragma implementation
#endif

#include "MethodB.h"

#include "Class.h"
#include "OrdColl.h"
#include "RegularExp.h"
#include "ClassManager.h"
#include "System.h"
#include "Buttons.h"
#include "Fields.h"
#include "Box.h"
#include "BorderItems.h"
#include "Alert_e.h"
#include "String.h"

#include "EtPeCmdNo.h"
#include "EtPeManager.h"

static PeMethodFilterDialog *filter;

//---- PeMethodItem ------------------------------------------------------------

NewMetaImpl(PeMethodItem, TextItem, (TP(cl), TP(mrp)));

PeMethodItem::PeMethodItem(PeMethodReference *m, char *label)
							: TextItem(cIdNone, label, gFixedFont, Point(2,1))
{
	mrp= m;
}

PeMethodItem::PeMethodItem(Class *cp)
			: TextItem(cIdNone, 0, gFixedFont->WithFace(eFaceBold), Point(2,1))
{
	mrp= 0;
	cl= cp;
	SetFString(FALSE, "Class: %s", cp->Name());
}

int PeMethodItem::Line()
{
	return mrp->Line();
}

Class *PeMethodItem::GetClass()
{
	return mrp ? mrp->GetClass() : 0;
}

bool PeMethodItem::IsPublic()
{
	return mrp->IsPublic();
}

//---- PeMethodBrowser -----------------------------------------------------------

NewMetaImpl(PeMethodBrowser, CollectionView,
							(TP(allMethods), TP(filteredMethods), TP(ofClass)));

PeMethodBrowser::PeMethodBrowser(EvtHandler *b)
									: CollectionView(b, 0, eCVDontStuckToBorder)
{
	allMethods= filteredMethods= 0;
	ofClass= 0;
	SetMinExtent(Point(250, 0));
}

PeMethodBrowser::~PeMethodBrowser()
{
	if (allMethods) {
		allMethods->FreeAll();
		SafeDelete(allMethods);
	}
}

static Object *AsMethodItem(Object*, Object *op, void*)
{
	PeMethodReference *mr= (PeMethodReference*)op;
	return new PeMethodItem(mr, (char*) mr->Str());
}

void PeMethodBrowser::ShowMethodsOf(Class* cp)
{
	SeqCollection *oc= 0, *methodsOfClass;

	if (cp != 0) {
		oc= new OrdCollection;
		ofClass= cp;
		for (Class *cl= cp; cl != 0; cl= cl->Super()) {
			oc->Add(new PeMethodItem(cl));
			SeqCollection *methods=
				Guard(gEtPeManager->LoadMethodsOfClass(cl), SeqCollection);
			if (methods) {
				methodsOfClass= (SeqCollection *) methods->Collect(AsMethodItem);
				oc->AddAll(methodsOfClass);
				SafeDelete(methodsOfClass);
			}
		}
	}
	if (allMethods)
		allMethods->FreeAll();
	SafeDelete(allMethods);
	allMethods= oc;
	SetCollection(allMethods, FALSE);
	UpdateEvent();
}

void PeMethodBrowser::SelectMethod(PeMethodReference *mr)
{
	PeMethodItem *mi;
	Collection *cp= GetCollection();
	if (cp) {
		Iter next(cp);
		for (int i= 0; (mi= (PeMethodItem*)next()) != 0; i++) {
			if (mi->mrp && mr->IsEqual(mi->mrp)) {
				Rectangle sr= Rectangle(0, i, 1, 1);
				SetSelection(sr);
				Rectangle r= ItemRect(sr);
				RevealAlign(r);
				return;
			}
		}
	}
}

class PeMethodItem *PeMethodBrowser::GetSelectedMethod()
{
	SeqCollection *col= GetCollection();
	if (GetSelection().IsEmpty())
		return 0;
	int at= GetSelection().origin.y;
	PeMethodItem *mi= Guard(col->At(at), PeMethodItem);
	return mi;
}

void PeMethodBrowser::DoSelect(Rectangle, int)
{
	PeMethodItem *mi= GetSelectedMethod();
	if (mi == 0)
		return;
	if (mi->mrp != 0)
		Control(cPeMethodBrowser, cPeChangedMethod, mi->mrp);
	else {
		Control(cPeMethodBrowser, cPeCLChangedClass, mi->cl);
		ClearSelection();
	}
}

void PeMethodBrowser::Filter()
{
	if (filter == 0)
		filter= new PeMethodFilterDialog();
	if (filter->ShowOnWindow(GetWindow()) == cIdYes) {
		SeqCollection *fp= filter->DoIt(allMethods);
		if (fp == 0)
			return;
		SetCollection(fp, FALSE);
		UpdateEvent();
		SafeDelete(filteredMethods);
		filteredMethods= fp;
	}
}

void PeMethodBrowser::RemoveFilter()
{
	if (filteredMethods != 0) {
		SetCollection(allMethods, FALSE);
		SafeDelete(filteredMethods);
		UpdateEvent();
	}
}

//---- Implementors -------------------------------------------------------

NewMetaImpl0(PeImplementors, CollectionView);

PeImplementors::PeImplementors(EvtHandler *b) : CollectionView(b, 0)
{
}

void PeImplementors::ShowImplementorsOf(PeMethodReference *mrp, bool subClassesOnly)
{
	SeqCollection *oc= new OrdCollection;
	Class *cl, *acl= mrp->GetClass();
	char *method= (char*)mrp->Str();

	Iter next(gClassManager->Iterator());
	while (cl= (Class*)next()) {
		if (subClassesOnly && !(cl->isKindOf(acl) && cl != acl))
			continue;
		Collection *methods= gEtPeManager->LoadMethodsOfClass(cl);
		if (methods) {
			Iter next1(methods);
			PeMethodReference *mr;
			while (mr= (PeMethodReference*)next1()) {
				if (gEtPeManager->SameMethods((char*)mr->Str(), method))
					oc->Add(new PeMethodItem(mr, form("%s::%s", cl->Name(), method)));
				else if (gEtPeManager->IsDestructor(method) && gEtPeManager->IsDestructor((char*)mr->Str()))
					oc->Add(new PeMethodItem(mr, form("%s::~%s", cl->Name(), cl->Name())));
			}
		}
	}
	SetCollection(oc);
	UpdateEvent();
}

void PeImplementors::ShowInherited(PeMethodReference *mrp)
{
	SeqCollection *oc= new OrdCollection;
	Class *cl, *ofClass= mrp->GetClass();
	char *methodName= (char*)mrp->Str();
	cl= ofClass->Super();

	for (; cl != 0; cl= cl->Super()) {
		Collection *methods= cl->GetMethods();
		if (methods == 0) {
			methods= gEtPeManager->LoadMethodsOfClass(cl);
			cl->SetMethods(methods);
		}
		Iter next1(methods);
		PeMethodReference *mr;
		while (mr= (PeMethodReference*)next1()) {
			if (gEtPeManager->SameMethods((char*)mr->Str(), methodName))
				oc->Add(new PeMethodItem(mr, form("%s::%s", cl->Name(), methodName)));
			else if (gEtPeManager->IsDestructor(methodName) && gEtPeManager->IsDestructor((char*)mr->Str()))
				oc->Add(new PeMethodItem(mr, form("%s::~%s", cl->Name(), cl->Name())));
		}
	}
	SetCollection(oc);
	UpdateEvent();
}

void PeImplementors::DoSelect(Rectangle, int)
{
	SeqCollection *col= GetCollection();
	int at= GetSelection().origin.y;
	PeMethodItem *mi= Guard(col->At(at), PeMethodItem);
	Control(cPeMethodBrowser, cPeChangedMethod, mi->mrp);
	ClearSelection();
}

//---- PeMethodFilterDialog ----------------------------------------------------

PeMethodFilterDialog::PeMethodFilterDialog() : Dialog("Method Filter")
{
	rex= new RegularExp;
	hidePublic= hidePrivate= hideProtected= FALSE;
}

VObject *PeMethodFilterDialog::DoMakeControls()
{
	return
		new VExpander(gPoint8,
			new TextItem("Filter (regular expression):"),
			pattern= new TextField(cIdFilter, 20),
			new BorderItem("Hide",
				new ManyOfCluster(cIdVisibility, eVObjVBase, gPoint4,
					"public",
					"protected",
					"private",
					0
				)
			),
			0
		);
}

VObject *PeMethodFilterDialog::DoMakeActions()
{
	return
		new HBox(Point(20), (VObjAlign)(eVObjVBase|eVObjHEqual),
			new ActionButton(cIdYes,    "Apply Filter", TRUE),
			new ActionButton(cIdCancel, "Cancel"),
			0
		);
}

void PeMethodFilterDialog::Control(int id, int p, void *v)
{
	switch (id) {

	case cIdVisibility:
		switch(p) {
		case cIdVisibility:
			hidePublic= !hidePublic;
			break;
		case cIdVisibility+1:
			hideProtected= !hideProtected;
			break;
		case cIdVisibility+2:
			hidePrivate= !hidePrivate;
			break;
		}
		break;
		
	default:
		Dialog::Control(id, p, v);
	}
}

SeqCollection *PeMethodFilterDialog::DoIt(Collection *allMethods)
{
	PeMethodItem *mi;
	bool matchAll= FALSE;
	char pat[1000];

	pattern->GetString(pat, sizeof pat);
	if (strlen(pat) == 0)
		matchAll= TRUE;
	else {
		rex->Reset(pat, TRUE);
		if (rex->GetExprState()) {
			ShowAlert(eAlertNote, rex->GetExprState());
			return 0;
		}
	}
	SeqCollection *filteredMethods= new OrdCollection;
	Iter next(allMethods);
	while (mi= (PeMethodItem *)next()) {
		int n, match= 0;
		if (mi->GetClass() == 0) {
			filteredMethods->Add(mi);
			continue;
		}
		if (!matchAll)
			match= rex->SearchForward(mi->AsString(), &n);
		if (matchAll || match != -1) {
			if (mi->IsPublic() && !hidePublic)
				filteredMethods->Add(mi);
			if (!mi->IsPublic() && !hidePrivate)
				filteredMethods->Add(mi);
		}
	}
	return filteredMethods;
}

